package xyz.xtt.asset.service.impl;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toSet;
import static xyz.xtt.common.asset.AssertUtils.isFalse;
import static xyz.xtt.common.asset.AssertUtils.isNull;
import static xyz.xtt.common.asset.AssertUtils.isTrue;
import static xyz.xtt.common.asset.AssertUtils.nonNull;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.core.util.Assert;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.api.enums.AssetTypeEnum;
import xyz.xtt.asset.api.enums.DealStateEnum;
import xyz.xtt.asset.api.enums.ReferProtocolEnum;
import xyz.xtt.asset.api.enums.SecondEnum;
import xyz.xtt.asset.api.enums.SellPatternEnum;
import xyz.xtt.asset.api.enums.TransactionTypeEnum;
import xyz.xtt.asset.client.request.CancelTransactionRequest;
import xyz.xtt.asset.client.request.ConfirmTransactionRequest;
import xyz.xtt.asset.client.request.MakeTransactionRequest;
import xyz.xtt.asset.client.request.MintRequest;
import xyz.xtt.asset.client.request.TransferActionRequest;
import xyz.xtt.asset.client.request.TransferFromRequest;
import xyz.xtt.asset.client.response.AssetInfo;
import xyz.xtt.asset.client.response.BurnResponse;
import xyz.xtt.asset.client.response.CancelOrderResponse;
import xyz.xtt.asset.client.response.ConfirmOrderResponse;
import xyz.xtt.asset.client.response.MintResponse;
import xyz.xtt.asset.client.response.MyNftPage;
import xyz.xtt.asset.client.response.MyNftResponse;
import xyz.xtt.asset.client.response.TransferActionResponse;
import xyz.xtt.asset.client.response.TransferFromResponse;
import xyz.xtt.asset.constants.AssetCache;
import xyz.xtt.asset.constants.ErrorMessage;
import xyz.xtt.asset.entity.TblAsset;
import xyz.xtt.asset.entity.TblAssetAccount;
import xyz.xtt.asset.entity.TblAssetExt;
import xyz.xtt.asset.entity.TblAssetStaticResource;
import xyz.xtt.asset.entity.TblAssetTransaction;
import xyz.xtt.asset.entity.TblAssetTransactionRecord;
import xyz.xtt.asset.enums.OrderTypeEnum;
import xyz.xtt.asset.enums.PayUnitEnum;
import xyz.xtt.asset.enums.TransactionSideEnum;
import xyz.xtt.asset.enums.TransferActionEnum;
import xyz.xtt.asset.event.ConfirmTransactionEventPublisher;
import xyz.xtt.asset.exception.ChainException;
import xyz.xtt.asset.request.AssetTransferFromRequest;
import xyz.xtt.asset.request.BatchTransferFromItem;
import xyz.xtt.asset.request.BatchTransferFromRequest;
import xyz.xtt.asset.request.BurnRequest;
import xyz.xtt.asset.response.InfoResponse;
import xyz.xtt.asset.response.TransactionResponse;
import xyz.xtt.asset.service.IInnerService;
import xyz.xtt.asset.service.INftService;
import xyz.xtt.asset.service.ITblAssetAccountService;
import xyz.xtt.asset.service.ITblAssetExtService;
import xyz.xtt.asset.service.ITblAssetNoService;
import xyz.xtt.asset.service.ITblAssetService;
import xyz.xtt.asset.service.ITblAssetStaticResourceService;
import xyz.xtt.asset.service.ITblAssetTransactionRecordService;
import xyz.xtt.asset.service.ITblAssetTransactionService;
import xyz.xtt.asset.service.IThirdService;
import xyz.xtt.asset.utils.BigDecimalUtils;
import xyz.xtt.asset.vo.AssetConvert;
import xyz.xtt.asset.vo.AssetNoMake;
import xyz.xtt.asset.vo.AssetSynMessageSaveItem;
import xyz.xtt.asset.vo.CreateGorupResult;
import xyz.xtt.asset.vo.CreateGroupRequest;
import xyz.xtt.asset.vo.MetaDataAttribute;
import xyz.xtt.asset.vo.MetaDataParams;
import xyz.xtt.asset.vo.TransferFromResult;
import xyz.xtt.common.asset.AssertUtils;
import xyz.xtt.common.objects.DozerUtil;
import xyz.xtt.common.starter.cache.CacheManager;
import xyz.xtt.pay.api.enums.PayOrderStatusEnum;
import xyz.xtt.user.client.response.UserInfoResponse;

/**
 * @author huqibo
 * @date 2024/12/24
 */
@RefreshScope
@Slf4j
@Service
public class NftServiceImpl implements INftService {
	@Autowired
	private ITblAssetService iTblAssetService;
	@Autowired
	private ITblAssetExtService iTblAssetExtService;
	@Autowired
	private ITblAssetStaticResourceService iTblAssetStaticResourceService;
	@Autowired
	private ITblAssetTransactionService iTblAssetTransactionService;
	@Autowired
	private ITblAssetAccountService iTblAssetAccountService;
	@Autowired
	private ITblAssetTransactionRecordService iTblAssetTransactionRecordService;
//	@Autowired
//	private ITblAssetEntitlementDeliverService iTblAssetEntitlementDeliverService;
//	@Autowired
//	private RedisLockUtil redisLockUtil;
//	@Autowired
//	private PayCenterHttpService payCenterHttpService;
	@Autowired
	private CacheManager cacheManager;
	@Autowired
	private ConfirmTransactionEventPublisher confirmTransactionEventPublisher;
	@Autowired
	private IInnerService iInnerService;
	@Autowired
	private IThirdService iThirdService;
	
	@Value("${asset.batchMin.size:100}")
	private Integer batchMintSize;

	@Value("${user.createGroup.defaultAppUserId}")
	private Long defaultAppUserId;

	@Autowired
	private ITblAssetNoService iTblAssetNoService;
	
	@Transactional(rollbackFor = Exception.class)
	@Override
	public MintResponse mint(MintRequest mintRequest, OrderTypeEnum orderType) {
//		AssetSynMessageSaveItem buildItem = AssetConvert.buildItem(mintRequest);
//		TblAsset asset = buildItem.getAsset();
//		TblAssetExt assetExt = buildItem.getAssetExt();
//		TblAsset assetInfo = this.iTblAssetService.infoWithBizIdByCache(mintRequest.getBizId());
//		nonNull(assetInfo, ErrorMessage.ASSET_EXIST, () -> log.info("bizId:{} exist", mintRequest.getBizId()));
//		boolean saveAsset = this.iTblAssetService.save(asset);
//		boolean saveExt = this.iblAssetExtService.save(assetExt);
//		boolean saveStatic = this.iTblAssetStaticResourceService.save(buildItem.getAssetStaticResource());
//		boolean baseSave = saveAsset && saveExt && saveStatic;
		AssetSynMessageSaveItem buildItem = saveBase(mintRequest);
		log.info("mint baseSave: {}", buildItem);
		TblAsset asset = buildItem.getAsset();
		TblAssetExt assetExt = buildItem.getAssetExt();
		String orderNo = mintRequest.getOrderNo();
		if (StringUtils.isBlank(orderNo)) {
			orderNo = IdUtil.getSnowflakeNextIdStr();
		}
		// 20250210修正数量错误
		TblAssetTransaction buildTransaction = iTblAssetTransactionService
				.buildTransaction(asset, asset.getOwnerId(), DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, new BigDecimal(
						String.valueOf(asset.getAmount())), orderNo, BigDecimal.ZERO, asset.getPrice(), BigDecimal.ZERO, asset.getBizType());

		if (assetExt.isErc1155()) {
			boolean saveTransaction = this.iTblAssetTransactionService.save(buildTransaction);
			isFalse(mintRequest.checkAmount(), ErrorMessage.MINT_EXCEPTION_WITH_AMOUNT);
			// 铸造只有增加方
			TblAssetTransactionRecord plus = this.iTblAssetAccountService.plus(buildTransaction, ReferProtocolEnum.convert(assetExt.getReferProtocol()),mintRequest.getVisiable());
			isNull(plus, ErrorMessage.TRANSFERFROM_FAIL, () -> log.info("mint plus is null param:{}", JSONUtil.toJsonStr(mintRequest)));
			boolean saveRecord = this.iTblAssetTransactionRecordService.save(plus);
			boolean check = saveTransaction && saveRecord;
			if (check) {
				return minResponse(asset.getId(), asset.getGoodsId(), buildTransaction.getOrderId(), mintRequest);
			}
		} else if (assetExt.isErc721()) {
			// 721 协议类型，数据只能是1个
			// 铸造的同时，帐户不能事先存在
			isFalse(mintRequest.checkAmountWithOne(), ErrorMessage.INVALID_QUANTITY, () -> log.info("param:{}", JSONUtil.toJsonStr(mintRequest)));
			Long ownerId = Longs.tryParse(asset.getOwnerId());
			TblAssetAccount ownerAssetInfo = this.iTblAssetAccountService.accountNoCheckAmount(asset.getId(), ownerId);
			nonNull(ownerAssetInfo, ErrorMessage.TARGET_ASSET_EXIST, () -> log.info("param: {}", JSONUtil.toJsonStr(mintRequest)));
			TblAssetAccount targetAccount = iTblAssetAccountService.initAccountOneAmount(asset.getId(), ownerId, ReferProtocolEnum.ERC_7211, BigDecimal.ONE, BigDecimal.ZERO);
			isNull(targetAccount, ErrorMessage.ACCOUNT_INIT_EXCEPTION);
			if (Objects.equals(orderType, OrderTypeEnum.NORMAL)) {
				buildTransaction.setGoodsId(String.valueOf(asset.getParentId()));
			}
			boolean check = this.iTblAssetTransactionService.save(buildTransaction);
			if (check) {
				return minResponse(asset.getId(), asset.getGoodsId(), buildTransaction.getOrderId(), mintRequest);
			}
		}
		return null;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public TblAssetTransaction repeatMint(Long userId, TblAsset asset, TblAssetExt assetExt, OrderTypeEnum orderType) {
		log.info("repeatMint baseSave asset :{}", asset);
		// 20250210修正数量错误
		TblAssetTransaction buildTransaction = iTblAssetTransactionService
				.buildTransaction(asset, String.valueOf(userId), DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, BigDecimal.ONE, IdUtil
						.getSnowflakeNextIdStr(), BigDecimal.ZERO, asset.getPrice(), BigDecimal.ZERO, asset.getBizType());
		boolean saveTransaction = this.iTblAssetTransactionService.save(buildTransaction);
		isFalse(saveTransaction, ErrorMessage.REMAINT_OPERATOR_FAIL);
		if (assetExt.isErc1155()) {
			// 铸造只有增加方
			TblAssetTransactionRecord plus = this.iTblAssetAccountService.plus(buildTransaction, ReferProtocolEnum.convert(assetExt.getReferProtocol()));
			isNull(plus, ErrorMessage.TRANSFERFROM_FAIL, () -> log.info("mint plus is null param:{}", JSONUtil.toJsonStr(asset)));
			boolean saveRecord = this.iTblAssetTransactionRecordService.save(plus);
			boolean check = saveTransaction && saveRecord;
			isFalse(check, ErrorMessage.REMAINT_OPERATOR_FAIL);
			return buildTransaction;
		} else if (assetExt.isErc721()) {
			// 721 协议类型，数据只能是1个
			// 铸造的同时，帐户不能事先存在
			TblAssetAccount ownerAssetInfo = this.iTblAssetAccountService.accountNoCheckAmount(asset.getId(), userId);
			nonNull(ownerAssetInfo, ErrorMessage.TARGET_ASSET_EXIST);
			TblAssetAccount targetAccount = iTblAssetAccountService.initAccountOneAmount(asset.getId(), userId, ReferProtocolEnum.ERC_7211, BigDecimal.ONE, BigDecimal.ZERO);
			isNull(targetAccount, ErrorMessage.ACCOUNT_INIT_EXCEPTION);
			if (Objects.equals(orderType, OrderTypeEnum.NORMAL)) {
				buildTransaction.setGoodsId(String.valueOf(asset.getParentId()));
			}
			boolean check = this.iTblAssetTransactionService.save(buildTransaction);
			isFalse(check, ErrorMessage.REMAINT_OPERATOR_FAIL);
			return buildTransaction;
		}
		return null;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public MintResponse create(MintRequest mintRequest) {
		// 创建入口全部是不上链的
		mintRequest.setUpChain(Boolean.FALSE);
//		AssetSynMessageSaveItem buildItem = AssetConvert.buildItem(mintRequest);
//		TblAsset asset = buildItem.getAsset();
//		TblAssetExt assetExt = buildItem.getAssetExt();
//		TblAsset assetInfo = this.iTblAssetService.infoWithBizIdByCache(mintRequest.getBizId());
//		nonNull(assetInfo, ErrorMessage.ASSET_EXIST, () -> log.info("bizId:{} exist", mintRequest.getBizId()));
//		boolean saveAsset = this.iTblAssetService.save(asset);
//		boolean saveExt = this.iblAssetExtService.save(assetExt);
//		boolean saveStatic = this.iTblAssetStaticResourceService.save(buildItem.getAssetStaticResource());
//		boolean baseSave = saveAsset && saveExt && saveStatic;
//		log.info("create baseSave: {}", baseSave);
//		if (baseSave) {
//			return minResponse(asset.getId(), asset.getGoodsId(), 0L, mintRequest);
//		}
		AssetSynMessageSaveItem saveBase = saveBase(mintRequest);
		return minResponse(saveBase.getAsset().getId(), saveBase.getAsset().getGoodsId(), 0L, mintRequest);
	}

	private AssetSynMessageSaveItem saveBase(MintRequest mintRequest) {
		AssetSynMessageSaveItem buildItem = AssetConvert.buildItem(mintRequest);
		TblAsset asset = buildItem.getAsset();
		TblAssetExt assetExt = buildItem.getAssetExt();
		TblAsset assetInfo = this.iTblAssetService.infoWithBizIdByCache(mintRequest.getBizId());
		nonNull(assetInfo, ErrorMessage.ASSET_EXIST, () -> log.info("bizId:{} exist", mintRequest.getBizId()));
		boolean saveAsset = this.iTblAssetService.save(asset);
		boolean saveExt = this.iTblAssetExtService.save(assetExt);
		boolean saveStatic = this.iTblAssetStaticResourceService.save(buildItem.getAssetStaticResource());
		boolean baseSave = saveAsset && saveExt && saveStatic;
		isFalse(baseSave, ErrorMessage.CREATE_EX);
		return buildItem;
	}

	@Override
	public MintResponse minResponse(Long assetId, String goodsId, Long transactionOrderId, MintRequest mintRequest) {
		cacheManager.delete(AssetCache.TBL_ASSET, goodsId);
		MintResponse response = DozerUtil.transfor(mintRequest, MintResponse.class);
		response.setAssetId(assetId);
		response.setTransactionOrderId(transactionOrderId);
		response.setUpChain(mintRequest.getUpChain());
		return response;
	}

//	private TblAssetTransaction buildTransaction(TblAsset asset, String buyerUserId, OrderTypeEnum orderType, Integer num, String orderNo) {
//		return iTblAssetTransactionService
//				.buildTransaction(asset, buyerUserId, DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, new BigDecimal(String.valueOf(num)), orderNo);
//	}
//
//	private TblAssetTransaction buildTransaction(TblAsset asset, DealStateEnum dealState, SellPatternEnum sellPatten, String buyerUserId,
//			OrderTypeEnum orderType, Integer num, String orderNo) {
//		return iTblAssetTransactionService.buildTransaction(asset, buyerUserId, dealState, orderType, sellPatten, new BigDecimal(String.valueOf(num)), orderNo);
//	}

	private TblAssetAccount buildTransactionRelation(TblAssetTransaction transaction) {
		long id = IdUtil.getSnowflakeNextId();
		return TblAssetAccount
				.builder()
				.id(id)
				.ownerId(Longs.tryParse(transaction.getBuyerUserId()))
				.amount(transaction.getAmount())
				.createTime(LocalDateTime.now())
				.build();
	}

	public Boolean batchSave(List<AssetSynMessageSaveItem> lists) {
//		List<List<AssetSynMessageSaveItem>> page = Lists.partition(lists, 1000);
//		for (List<AssetSynMessageSaveItem> list : page) {
//			List<TblAsset> assetList = Lists.newArrayList();
//			List<TblAssetExt> assetExtList = Lists.newArrayList();
//			List<TblAssetStaticResource> staticResourceList = Lists.newArrayList();
//			List<TblAssetTransaction> transactionList = Lists.newArrayList();
//			List<TblAssetAccount> transactionRelationList = Lists.newArrayList();
//			for (AssetSynMessageSaveItem item : list) {
//				TblAsset asset = item.getAsset();
//				TblAssetExt assetExt = item.getAssetExt();
//				assetList.add(asset);
//				assetExtList.add(assetExt);
//				staticResourceList.add(item.getAssetStaticResource());
//				TblAssetTransaction buildTransaction = iTblAssetTransactionService.buildTransaction(asset, asset.getOwnerId(), OrderTypeEnum.MINT, asset.getAmount(), StringUtils.EMPTY);
//				transactionList.add(buildTransaction);
//				// 如果是1155,处理拆分关系
//				if (assetExt.isErc1155()) {
//					transactionRelationList.add(buildTransactionRelation(buildTransaction));
//				}
//			}
//			if (CollectionUtil.isNotEmpty(assetList)) {
//				this.iTblAssetService.saveBatch(assetList);
//				this.iblAssetExtService.saveBatch(assetExtList);
//				this.iTblAssetStaticResourceService.saveBatch(staticResourceList);
//				this.iTblAssetTransactionService.saveBatch(transactionList);
//			}
//			if (CollectionUtil.isNotEmpty(transactionRelationList)) {
//				iTblAssetAccountService.saveBatch(transactionRelationList);
//			}
//		}
		return Boolean.TRUE;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Boolean batchMint(List<MintRequest> mintRequest) {
		isTrue(mintRequest.size() > batchMintSize, null);
//		List<AssetSynMessageSaveItem> lists = mintRequest.stream().map(MintRequest::buildItem).collect(toList());
		return batchSave(Lists.newArrayList());
	}

	// 前置检查
	private void beforeCheck(Long from, Long to) {
		UserInfoResponse fromUser = iInnerService.userInfo(from);
//		TblUser fromUser = this.iTblUserService.info(from);
		AssertUtils.isNull(fromUser, ErrorMessage.USER_NOT_EXIST, () -> log.info("from user not exist uid:{}", from));
		if (Objects.equals(to, 0L)) {
			return;
		}
		UserInfoResponse toUser = iInnerService.userInfo(to);
		AssertUtils.isNull(toUser, ErrorMessage.USER_NOT_EXIST, () -> log.info("to user not exist uid:{}", to));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public TransferFromResponse transferFrom(String platform, TransferFromRequest transferFromRequest) {
		log.info("transferFrom  TransferFromRequest:{}", transferFromRequest);
		Boolean needConfirm = transferFromRequest.getNeedConfirm();
		if (StringUtils.isBlank(transferFromRequest.getOrderType())) {
			// 没有订单类型，就定义为普通定单
			transferFromRequest.setOrderType(OrderTypeEnum.ACTIVITY.name());
		}
		//根据bizId 获取资产信息
		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(transferFromRequest.getBizId());
		isNull(asset, ErrorMessage.ASSET_NOT_EXIST, ()->log.info("bizId:{} not exist", transferFromRequest.getBizId()));
		MakeTransactionRequest makeOrder = MakeTransactionRequest.getMakeOrder(platform, transferFromRequest, asset.getId());
		TblAssetTransaction transaction = makeTransaction(makeOrder);

		TransferFromResponse response = DozerUtil.transfor(transferFromRequest, TransferFromResponse.class);
		response.setAssetId(transaction.getAssetId());
		response.setTransactionOrderId(transaction.getOrderId());

		if (Objects.equals(Boolean.TRUE, needConfirm)) {
			return response;
		}
		ConfirmTransactionRequest confirmTransactionRequest = getConfirmTransactionRequest(transaction);
		log.info("transferFrom  ConfirmTransactionRequest:{}", confirmTransactionRequest);
		confirmTransaction(confirmTransactionRequest);
		return response;


//		String orderNo = transferFromRequest.getOrderNo();
//		TblAssetTransaction infoWithOrderNo = this.iTblAssetTransactionService.infoWithOrderNo(orderNo);
//		nonNull(infoWithOrderNo, ErrorMessage.REPEAT_TRANSACTION_ORDER_NO);
//		Long from = transferFromRequest.getFrom();
//		Long to = transferFromRequest.getTo();
//		isTrue(Objects.equals(from, to), ErrorMessage.TRANSACTION_SAME_USER);
//		beforeCheck(from, to);
//		String bizId = transferFromRequest.getBizId();
//		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(bizId);
//		isNull(asset, ErrorMessage.ASSET_NOT_EXIST);
//		Long id = asset.getId();
//		// 如果需要确认
//		BigDecimal fees = transferFromRequest.getFees();
//		String feesVal = Objects.isNull(fees) ? "0" : fees.toPlainString();
//		String price = StringUtils.isBlank(transferFromRequest.getPrice()) ? "0" : transferFromRequest.getPrice();
//
//		//TOOD 编号 处理
//
//		if (Objects.equals(Boolean.TRUE, needConfirm)) {
//			log.info("transferFrom:{}", JSONUtil.toJsonStr(transferFromRequest));
//			Integer num = Objects.isNull(transferFromRequest.getAmount()) ? 0 : transferFromRequest.getAmount();
//			BigDecimal actTotalPrice = new BigDecimal(String.valueOf(price)).multiply(new BigDecimal(String.valueOf(num)));
//			MakeTransactionRequest makeOrder = MakeTransactionRequest
//					.builder()
//					.from(transferFromRequest.getFrom())
//					.to(transferFromRequest.getTo())
//					.num(num)
//					.assetId(id)
//					.orderNo(transferFromRequest.getOrderNo())
//					.price(transferFromRequest.getPrice())
//					.orderType(transferFromRequest.getOrderType())
//					.fees(feesVal)
//					.orderPrice(actTotalPrice)
//					.actTotalPrice(actTotalPrice.add(new BigDecimal(feesVal)))
//					.bizType(platform)
//					.build();
//			TblAssetTransaction makeTransaction = makeTransaction(makeOrder);
//			TransferFromResponse response = DozerUtil.transfor(transferFromRequest, TransferFromResponse.class);
//			response.setAssetId(makeTransaction.getAssetId());
//			response.setTransactionOrderId(makeTransaction.getOrderId());
//			return response;
//		}
//		TblAssetExt assetExt = iTblAssetExtService.infoWithAssetIdByCache(asset.getId());
//		isNull(assetExt, ErrorMessage.ASSET_NOT_EXIST);
//		isFalse(assetExt.checkUpChain(), ErrorMessage.NOT_CHAIN_UP);
//		TblAssetAccount fromAccount = this.iTblAssetAccountService.accountNoCheckAmount(id, from);
//		isNull(fromAccount, ErrorMessage.ASSET_NOT_EXIST);
//		ReferProtocolEnum referProtocol = fromAccount.referProtocol();
//		OrderTypeEnum orderType = OrderTypeEnum.convert(transferFromRequest.getOrderType());
//		isNull(orderType, ErrorMessage.ORDER_TYPE_ERR);
//
//		if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_1155)) {
//			asset.setPrice(new BigDecimal(price));
//			// 会判断交易双方是不是在平台有用户
//			asset.setOwnerId(String.valueOf(transferFromRequest.getFrom()));
//			BigDecimal amount = new BigDecimal(String.valueOf(transferFromRequest.getAmount()));
//			TblAssetTransaction buildTransaction = iTblAssetTransactionService
//					.buildTransaction(asset, String.valueOf(to), DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, amount, transferFromRequest
//							.getOrderNo(), fees, asset.getPrice(), asset.getPrice().add(fees), asset.getBizType());
////			TblAssetTransaction buildTransaction = iTblAssetTransactionService.buildTransaction(asset, String.valueOf(to), orderType, transferFromRequest.getAmount(), orderNo);
//			isNull(buildTransaction, ErrorMessage.USER_INFO_NOT_EXIST, () -> log.info("transaction is null param:{}", JSONUtil.toJsonStr(transferFromRequest)));
//			Boolean transferFrom = this.iTblAssetAccountService.transferFrom(buildTransaction, referProtocol);
//			isFalse(transferFrom, ErrorMessage.UNKNOWN_ERROR);
//			TransferFromResponse response = DozerUtil.transfor(transferFromRequest, TransferFromResponse.class);
//			response.setAssetId(id);
//			response.setTransactionOrderId(buildTransaction.getOrderId());
//			return response;
//		} else if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_7211)) {
//			TblAssetTransaction buildTransaction = iTblAssetTransactionService
//					.buildTransaction(asset, String
//							.valueOf(to), DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, BigDecimal.ONE, orderNo, transferFromRequest
//									.getFees(), asset.getPrice(), asset.getPrice().add(transferFromRequest.getFees()), asset.getBizType());
//			// 721类型 必须和资产的拥有人必须和转出人是一个人
//			isFalse(Objects.equals(asset.getOwnerId(), String.valueOf(fromAccount.getOwnerId())), ErrorMessage.ASSET_NOT_EXIST);
//			Boolean updateAssetOwner = this.iTblAssetService.updateOwner(String.valueOf(fromAccount.getOwnerId()), String.valueOf(to), id);
//			isFalse(updateAssetOwner, ErrorMessage.UNKNOWN_ERROR);
//			// 缓存清除
//			cacheManager.delete(AssetCache.TBL_ASSET, asset.getGoodsId());
//			Boolean updateAccountOwner = this.iTblAssetAccountService.updateOwner(fromAccount.getAccountNo(), fromAccount.getOwnerId(), to);
//			isFalse(updateAccountOwner, ErrorMessage.UNKNOWN_ERROR);
//			TransferFromResponse response = DozerUtil.transfor(transferFromRequest, TransferFromResponse.class);
//			response.setAssetId(id);
//			boolean save = this.iTblAssetTransactionService.save(buildTransaction);
//			isFalse(save, ErrorMessage.TRANSFERFROM_FAIL);
//			return response;
//		}
//		return null;
	}

	/**
	 * 根据订单表获取记录
	 * @param transaction
	 * @return
	 */
	public static ConfirmTransactionRequest getConfirmTransactionRequest(TblAssetTransaction transaction) {
		return ConfirmTransactionRequest.builder()
				.orderId(transaction.getOrderId())
				.from(Long.valueOf(transaction.getSellerUserId()))
				.to(Long.valueOf(transaction.getBuyerUserId()))
				.assetId(transaction.getAssetId())
				.actTotalPrice(transaction.getActTotalPrice())
				.build();
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Boolean accountTransferFrom(AssetTransferFromRequest assetTransferFromRequest) {
		isTrue(Objects.equals(assetTransferFromRequest.getFrom(), assetTransferFromRequest.getTo()), ErrorMessage.TRANSACTION_SAME_USER);
		TblAssetAccount from = this.iTblAssetAccountService.accountNoCheckAmount(assetTransferFromRequest.getAssetId(), assetTransferFromRequest.getFrom());
		isNull(from, ErrorMessage.ACCOUNT_NOT_EXIST, () -> log.info("from account is null"));
		ReferProtocolEnum referProtocol = from.referProtocol();
		if (referProtocol == ReferProtocolEnum.ERC_1155) {
			BigDecimal price = Objects.isNull(from.getPrice()) ? BigDecimal.ZERO : from.getPrice();
			TblAssetAccount to = this.iTblAssetAccountService.accountIsNullCreate(assetTransferFromRequest.getAssetId(), assetTransferFromRequest.getTo(),price);
			boolean sameReferProtocol = Objects.equals(from.getReferProtocol(), to.getReferProtocol());
			isFalse(sameReferProtocol, ErrorMessage.ACCOUNT_REFER_PROTOCAL_NOT_SAME, () -> log
					.info("from:{},to:{}", JSONUtil.toJsonStr(from), JSONUtil.toJsonStr(to)));
			isFalse(Objects.equals(from.getAssetId(), to.getAssetId()), ErrorMessage.ACCOUNT_ASSET_NOT_SAME, () -> log
					.info("from:{},to:{}", JSONUtil.toJsonStr(from), JSONUtil.toJsonStr(to)));
			TblAssetTransaction buildTransaction = iTblAssetTransactionService
					.buildTransaction(from, to, assetTransferFromRequest.getNum(), assetTransferFromRequest
							.getOrderNo(), OrderTypeEnum.ACTIVITY, DealStateEnum.PAID, BigDecimal.ZERO, PayUnitEnum.UN_KNOW, assetTransferFromRequest
									.getOrderPirce(), assetTransferFromRequest.getBizType());
			isNull(buildTransaction, ErrorMessage.USER_INFO_NOT_EXIST, () -> log.info("transaction is null param:{}", JSONUtil.toJsonStr(buildTransaction)));
			Boolean transferFrom = this.iTblAssetAccountService.transferFrom(buildTransaction, referProtocol);
			isFalse(transferFrom, ErrorMessage.UNKNOWN_ERROR);
//			return TransferFromResponse
//					.builder()
//					.from(assetTransferFromRequest.getFrom())
//					.to(assetTransferFromRequest.getTo())
//					.amount(assetTransferFromRequest.getNum())
//					.bizId(buildTransaction.getGoodsId())
//					.orderNo(buildTransaction.getOrderNo())
//					.price(String.valueOf(buildTransaction.getOrderPrice()))
//					.assetId(buildTransaction.getAssetId())
//					.transactionOrderId(buildTransaction.getOrderId())
//					.build();
			return Boolean.TRUE;
		} else if (referProtocol == ReferProtocolEnum.ERC_7211) {
			Boolean updateOwner = this.iTblAssetAccountService.updateOwner(from.getAccountNo(), from.getOwnerId(), assetTransferFromRequest.getTo());
			isFalse(updateOwner, ErrorMessage.UNKNOWN_ERROR);
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

//	private Boolean transferFrom721(String fromUid, TblAsset asset, Long from, Long to, Integer num) {
//		// 721类型的，数量必定是1 1相等就可以
//		Assert.isTrue(num.intValue() == 1, "721 num is 1");
//		Assert.isTrue(Objects.equals(asset.getAmount(), 1) && Objects.equals(asset.getOwnerId(), fromUid), "721 num is 1");
//
//		TblAssetTransaction buildTransaction = buildTransaction(asset, String.valueOf(to), OrderTypeEnum.NORMAL, 1, StringUtils.EMPTY);
//		this.iTblAssetService.updateOwner(String.valueOf(from), String.valueOf(to), asset.getId());
//		this.iTblAssetTransactionService.save(buildTransaction);
//		return true;
//	}

	@Override
	public Boolean batchTransferFrom(String uid, BatchTransferFromRequest batchTransferFromRequest) {
		String bizId = batchTransferFromRequest.getBizId();
		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(bizId);
		TblAssetExt assetExt = this.iTblAssetExtService.infoWithAssetIdByCache(asset.getId());
		List<BatchTransferFromItem> to = batchTransferFromRequest.getTo();
		if (assetExt.isErc1155()) {
			List<TblAssetAccount> ownerList = this.iTblAssetAccountService.ownerList(asset.getId(), batchTransferFromRequest.getFrom());
			BigDecimal ownNum = ownerList.stream().map(TblAssetAccount::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
			Integer total = to.stream().map(BatchTransferFromItem::getNum).mapToInt(Integer::intValue).sum();
			// 拥有总数，大于持有总数
			Assert.isTrue(ownNum.intValue() >= total.intValue(), "Insufficient quantity possessed");
//			for (BatchTransferFromItem target : to) {
//				TblAssetTransaction buildTransaction = iTblAssetTransactionService
//						.buildTransaction(asset, String.valueOf(to), DealStateEnum.PAID, orderType, SellPatternEnum.NORMAL, asset
//								.getPrice(), transferFromRequest.getOrderNo(), BigDecimal.ZERO, asset.getPrice(), asset.getBizType());
//				iTblAssetAccountService.move(ownerList, buildTransaction.getAssetId(), batchTransferFromRequest.getFrom(), target.getTo(), target.getNum());
//				this.iTblAssetTransactionService.save(buildTransaction);
//			}
		} else {
			// transferFrom721(uid, asset, batchTransferFromRequest.getFrom(),
			// to.get(0).getTo(), to.get(0).getNum());
		}
		return Boolean.TRUE;
	}

	@Override
	public Boolean packageTransferFrom(String uid, List<TransferFromRequest> transferFromRequestList) {
		Map<Long, List<TransferFromRequest>> groupFrom = transferFromRequestList.stream().collect(groupingBy(TransferFromRequest::getFrom));
		return null;
	}

	@Override
	public BurnResponse burn(String platform, BurnRequest burnRequest) {
		log.info("burn platform:{},burnRequest:{}", platform, JSONUtil.toJsonStr(burnRequest));
		int amount = burnRequest.getAmount().intValue();
		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(burnRequest.getBizId());
		isNull(asset, ErrorMessage.ASSET_NOT_EXIST);
		isFalse(asset.getBizType().equals(platform), ErrorMessage.BIZ_TYPE_NOT_OWN);
		TblAssetExt infoWithAssetIdByCache = this.iTblAssetExtService.infoWithAssetIdByCache(asset.getId());
		ReferProtocolEnum referProtocol = infoWithAssetIdByCache.referProtocol();
		isNull(referProtocol, ErrorMessage.UPSUPPORTED_PROTOCOL_TYPE);
		TblAssetTransaction infoWithOrderNo = this.iTblAssetTransactionService.infoWithOrderNo(burnRequest.getOrderNo());
		nonNull(infoWithOrderNo, ErrorMessage.REPEAT_TRANSACTION_ORDER_NO);
		TblAssetAccount from = this.iTblAssetAccountService.accountNoCheckAmount(asset.getId(), Longs.tryParse(burnRequest.getOwnerId()));
		isNull(from, ErrorMessage.ACCOUNT_NOT_EXIST);
		TblAssetTransaction buildTransaction = iTblAssetTransactionService
				.buildTransaction(from, 0L, amount, burnRequest
						.getOrderNo(), OrderTypeEnum.BURN, DealStateEnum.PAID, BigDecimal.ZERO, PayUnitEnum.UN_KNOW, BigDecimal.ZERO, platform);
		TblAssetTransactionRecord minus = this.iTblAssetAccountService.minus(buildTransaction, referProtocol);
		isNull(minus, ErrorMessage.BURN_OPERATORO_EX);
		boolean save = this.iTblAssetTransactionService.save(buildTransaction);
		isFalse(save, ErrorMessage.BURN_OPERATORO_EX);
		BurnResponse response = DozerUtil.transfor(burnRequest, BurnResponse.class);
		response.setAssetId(asset.getId());
		response.setTransactionOrderId(buildTransaction.getOrderId());
		return response;
	}

	@Override
	public InfoResponse query(String uid, String busiId) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<TransactionResponse> transferFromList(String uid, String type, Integer currPage, Integer pageSize) {
		// TODO Auto-generated method stub
		return null;
	}

	private boolean checkTarget(TblAssetAccount targetAccount) {
		return Optional.ofNullable(targetAccount).map(TblAssetAccount::getAmount).map(BigDecimalUtils::isGreater).orElse(Boolean.FALSE);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public TransferFromResult transferFromWithActivity(Long accountNo, Long to, String orderNo, Integer amount, Long entitlementId, String bizType) {
		TblAssetAccount fromAccount = this.iTblAssetAccountService.ownerAssetForUpdate(accountNo);
		isNull(fromAccount, ErrorMessage.ASSET_NOT_EXIST, () -> log.info("buildTransaction from is null fromAccount:{}", fromAccount));
		isTrue(fromAccount.isLock(), ErrorMessage.ACCOUNT_LOCK, () -> log.info("account: {} is lock", fromAccount.getAccountNo()));
		// 查询，看目标帐户是不是已经有了
		// 就算有，也有可能不是活动来了，别人送的，或者其它来源
		// 应用从活动上来查
		if (Objects.nonNull(entitlementId) && entitlementId.longValue() > 0) {
			Boolean deliverCheck = iInnerService.deliverCheck(entitlementId, to);
			if (Objects.equals(deliverCheck, Boolean.TRUE)) {
				log.info("transferFromWithActivity orderNo:{} target to:{},amount:{},entitlementId:{}  return false", orderNo, to, amount, entitlementId);
				return TransferFromResult.builder().result(false).build();
			}
//			TblAssetEntitlementDeliver deliver = this.iTblAssetEntitlementDeliverService.deliver(entitlementId, to);
//			// 有记录就不让兑换了
//			if (Objects.nonNull(deliver)) {
//				log.info("transferFromWithActivity orderNo:{} target to:{},amount:{},entitlementId:{}  return false", orderNo, to, amount, entitlementId);
//				return TransferFromResult.builder().result(false).build();
//			}
		}
		TblAssetAccount targetAccount = this.iTblAssetAccountService.ownerAssetForUpdate(fromAccount.getAssetId(), to);
		if (Objects.isNull(targetAccount)) {
			ReferProtocolEnum convertWithCode = ReferProtocolEnum.convertWithCode(fromAccount.getReferProtocol());
			targetAccount = this.iTblAssetAccountService.initAccount(fromAccount.getAssetId(), to, convertWithCode, BigDecimal.ZERO);
			AssertUtils.isNull(targetAccount, ErrorMessage.ASSET_NOT_EXIST, () -> log.info("orderNo:{} buildTransaction to is null", orderNo));
		}
		TblAsset asset = this.iTblAssetService.infoWithIdByCache(fromAccount.getAssetId());
		isNull(asset, ErrorMessage.ACCOUNT_LOCK, () -> log.info("asset: {} is null", fromAccount.getAssetId()));
		TblAssetTransaction transaction = iTblAssetTransactionService
				.buildTransactionActivity(fromAccount, targetAccount, amount, orderNo, asset.getPrice(), bizType);
		// 活动类实付金额全是 0
		transaction.setActTotalPrice(BigDecimal.ZERO);
		boolean saveTransaction = this.iTblAssetTransactionService.save(transaction);
		Boolean transferFrom = iTblAssetAccountService.transferFrom(transaction, fromAccount, targetAccount, OrderTypeEnum.ACTIVITY, orderNo);
		boolean result = transferFrom && saveTransaction;
		TransferFromResult transfer = TransferFromResult
				.builder()
				.result(result)
				.assetId(transaction.getAssetId())
				.from(fromAccount.getAccountNo())
				.to(targetAccount.getAccountNo())
				.num(transaction.getAmount().intValue())
				.transaction(transaction)
				.build();
		isFalse(result, ErrorMessage.TRANSFERFROM_FAIL, () -> log
				.info("bransferFrom orderNo:{},entitlementId:{},fail: {} ", orderNo, entitlementId, JSONUtil.toJsonStr(transfer)));
		log.info("transferFromWithActivity transfer:{},result:{}", transfer, result);
		return transfer;
	}

	private TblAssetTransaction buildNewTransaction(TblAsset asset, String buyerUserId, OrderTypeEnum orderType, Integer num, String orderNo, BigDecimal fees,
			BigDecimal orderPrice, BigDecimal actTotalPrice, String bizType) {
		return this.iTblAssetTransactionService
				.buildTransaction(asset, buyerUserId, DealStateEnum.NOT_PAYED, orderType, SellPatternEnum.NORMAL, new BigDecimal(
						String.valueOf(num)), orderNo, fees, orderPrice, actTotalPrice, bizType);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public TblAssetTransaction makeTransaction(MakeTransactionRequest makeOrder) {
		log.info("makeTransaction:{}", JSONUtil.toJsonStr(makeOrder));
		// 卖方
		Long fromUid = makeOrder.getFrom();
		// 买方
		Long toUid = makeOrder.getTo();
		Integer purchaseNum = makeOrder.getNum();
		isTrue(makeOrder.getAssetNoId() != null && purchaseNum > 1,ErrorMessage.ARGUMENT_NOT_VALID);
		beforeCheck(fromUid, toUid);
		Long assetId = makeOrder.getAssetId();
		TblAsset asset = this.iTblAssetService.getById(assetId);
		isNull(asset, ErrorMessage.ASSET_NOT_EXIST);
		if (StringUtils.isBlank(makeOrder.getOrderNo())) {
			makeOrder.setOrderNo(IdUtil.getSnowflakeNextIdStr());
		}
		OrderTypeEnum orderType = OrderTypeEnum.convert(makeOrder.getOrderType());
		isNull(orderType, ErrorMessage.UN_SUPPORTED_ORDER_TYPE, () -> log.info("不支持的订单类型 param:{}", JSONUtil.toJsonStr(makeOrder)));
		BigDecimal fees = StringUtils.isBlank(makeOrder.getFees()) ? BigDecimal.ZERO : new BigDecimal(makeOrder.getFees());
		// 用户转移不在是资产上的拥有人了
		if (!Objects.equals(asset.getOwnerId(), String.valueOf(fromUid))) {
			asset.setOwnerId(String.valueOf(fromUid));
		}
		TblAssetTransaction buildTransaction = buildNewTransaction(asset, String.valueOf(toUid), orderType, makeOrder.getNum(), makeOrder
				.getOrderNo(), fees, makeOrder.getOrderPrice(), makeOrder.getOrderPrice().add(fees), makeOrder.getBizType());
		isNull(buildTransaction, ErrorMessage.USER_INFO_NOT_EXIST, () -> log.error("makeTransaction is null param:{}", JSONUtil.toJsonStr(makeOrder)));
		isTrue(buildTransaction.checkSide(), ErrorMessage.CHECK_TRANSACTION_EX);
		buildTransaction.setRemark(makeOrder.getRemark());
		Boolean result = Boolean.FALSE;
		// 普通订单需要操作资产数量转移
		// 铸造类型的订单，有支付单类型的，可能目标帐户都不存在
		// 铸造类型的订单，没有转出方 也就 没有fromAccount
		// 不能从帐户上获取资产类型
		// 铸造类型的，只创建交易单就算完成
		TransactionTypeEnum transactionType = buildTransaction.transactionType();
		if (Objects.equals(transactionType, TransactionTypeEnum.MINT)) {
			result = Boolean.TRUE;
		} else {
			TblAssetAccount fromAccount = this.iTblAssetAccountService.accountNoCheckAmount(assetId, fromUid);
			isNull(fromAccount, ErrorMessage.ASSET_NOT_EXIST);
			isTrue(fromAccount.getAmount().intValue() < makeOrder.getNum(), ErrorMessage.STOCK_NOT_ENOUGH, () -> log
					.info("makeOrder num:{} is bigger than account num:{}. assetId:{}", makeOrder.getNum(), fromAccount.getAmount(), assetId));
			ReferProtocolEnum referProtocol = fromAccount.referProtocol();
			TblAssetTransactionRecord minus = this.iTblAssetAccountService.minus(buildTransaction, referProtocol);
			if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_1155)) {
				// 订单卖完下架
//				asset.setPrice(new BigDecimal(makeOrder.getPrice()));
				isNull(minus, ErrorMessage.TRANSFERFROM_FAIL, () -> log.error("makeOrder uid:{} minus plus is null"));
				// 1155 需要交易记录
				result = this.iTblAssetTransactionRecordService.save(minus);
			} else if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_7211)) {
//				isFalse(makeOrder.checkNumWithOne(), ErrorMessage.INVALID_QUANTITY, () -> log.info("param:{}", JSONUtil.toJsonStr(makeOrder)));
				// 721类型 必须和资产的拥有人必须和转出人是一个人
				isFalse(Objects.equals(asset.getOwnerId(), String.valueOf(fromUid)), ErrorMessage.ASSET_NOT_EXIST);
				result = true;
//				result = this.iTblAssetAccountService.updateBalance(fromAccount.getAccountNo(), fromUid, BigDecimal.ONE, BigDecimal.ZERO);
//				isFalse(result, ErrorMessage.ASSET_NOT_EXIST);
			}
		}
		if (result) {
			boolean saveResult = this.iTblAssetTransactionService.save(buildTransaction);
			isFalse(saveResult, ErrorMessage.TRANSATION_CREATE_FAIL, () -> log
					.error("makeOrder uid:{} param:{} save transaction failed", JSONUtil.toJsonStr(makeOrder)));
			if (!Objects.equals(asset.getFirstType(),AssetTypeEnum.BLIND_BOX.code())){
				// 下单锁定资产编号
				AssetNoMake assetNoMake = AssetNoMake.builder()
						.rootAssetId(asset.getRootAssetId())
						.assetId(makeOrder.getAssetId())
						.form(makeOrder.getFrom())
						.to(makeOrder.getTo())
						.transactionId(buildTransaction.getOrderId())
						.assetNoId(makeOrder.getAssetNoId())
						.purchaseNum(purchaseNum)
						.assetSource(makeOrder.getAssetSource())
						.isTopTrade(isTopTrade(asset,makeOrder.getFrom()))
						.build();
				iTblAssetNoService.syncAssetNoLock(assetNoMake);
			}
			return buildTransaction;
		}
		log.error("makeOrder param:{} transfer from failed", JSONUtil.toJsonStr(makeOrder));
		throw new ChainException(ErrorMessage.TRANSFERFROM_FAIL);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public CancelOrderResponse cancelTransaction(CancelTransactionRequest cancelTransactionRequest) {
		log.info("cancelTransaction:{}", JSONUtil.toJsonStr(cancelTransactionRequest));
		Long orderId = cancelTransactionRequest.getOrderId();
		Long from = cancelTransactionRequest.getFrom();
		Long to = cancelTransactionRequest.getTo();
		TblAssetTransaction transaction = this.iTblAssetTransactionService.queryTblAssetTransaction(orderId, from, to);
		isNull(transaction, ErrorMessage.TRANSATION_NOT_EXIST, () -> log
				.info("cancelUnPayOrder param:{} transaction is null", JSONUtil.toJsonStr(cancelTransactionRequest)));
		isFalse(transaction.checkCancel(), ErrorMessage.TRANSATION_STATE_EX, () -> log.info("transaction: {}", JSONUtil.toJsonStr(transaction)));
		isTrue(transaction.checkSide(), ErrorMessage.CHECK_TRANSACTION_EX);

		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(transaction.getGoodsId());

		// 资产编号信息
		AssetNoMake assetNoMake = AssetNoMake.builder()
				.transactionId(orderId)
				.source(PayOrderStatusEnum.PAY_FAIL)
				.form(from)
				.to(to)
				.isTopTrade(isTopTrade(asset,from))
				.build();
		iTblAssetNoService.syncAssetLockCancel(assetNoMake);
		return cancelTransaction(cancelTransactionRequest, transaction);
	}
	private CancelOrderResponse cancelTransaction(CancelTransactionRequest cancelTransactionRequest, TblAssetTransaction transaction) {
		Long orderId = cancelTransactionRequest.getOrderId();
		Long from = cancelTransactionRequest.getFrom();
		Long to = cancelTransactionRequest.getTo();
		TransactionTypeEnum transactionType = transaction.transactionType();
		log.info("cancelTransaction param:{},transactionType:{}", JSONUtil.toJsonStr(cancelTransactionRequest), JSONUtil.toJsonPrettyStr(transactionType));
		// 铸造类的交易取消，不用改余额只取消交易单就可以了
		if (Objects.equals(transactionType, TransactionTypeEnum.NORMAL)) {
			// 721类型的退单，余额是0，不能检查余额
			TblAssetAccount fromAccount = this.iTblAssetAccountService.accountNoCheckAmount(transaction.getAssetId(), from);
			isNull(fromAccount, ErrorMessage.ASSET_NOT_EXIST);
			ReferProtocolEnum referProtocol = fromAccount.referProtocol();
			if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_1155)) {
				// 回退状态,原帐户一定是存在的,不会存在帐户未初始化的情况
				TblAssetTransactionRecord record = this.iTblAssetAccountService.accountOpertor(transaction, fromAccount, TransactionSideEnum.BUY);
				boolean saveRecord = this.iTblAssetTransactionRecordService.save(record);
				isFalse(saveRecord, ErrorMessage.CANCEL_ORDER_EX, () -> log.error("saveRecord is false. record:{}", JSONUtil.toJsonStr(record)));
			} else if (Objects.equals(referProtocol, ReferProtocolEnum.ERC_7211)) {
				// 取消订单变买方
				Boolean updateBalance = this.iTblAssetAccountService.updateBalance(fromAccount.getAccountNo(), from, BigDecimal.ZERO, BigDecimal.ONE);
				isFalse(updateBalance, ErrorMessage.CANCEL_ORDER_EX);
			}
		}
		Boolean upateTransactionDealState = this.iTblAssetTransactionService
				.upateTransactionDealState(orderId, from, to, DealStateEnum.NOT_PAYED, DealStateEnum.CANCEL, null);
		isFalse(upateTransactionDealState, ErrorMessage.CANCEL_ORDER_EX);
		CancelOrderResponse response = DozerUtil.transfor(cancelTransactionRequest, CancelOrderResponse.class);
		response.setTs(System.currentTimeMillis());
		return response;
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public ConfirmOrderResponse confirmTransaction(ConfirmTransactionRequest confirmTransactionRequest) {
		log.info("confirmTransaction:{}", JSONUtil.toJsonStr(confirmTransactionRequest));
		Long orderId = confirmTransactionRequest.getOrderId();
		Long from = confirmTransactionRequest.getFrom();
		Long to = confirmTransactionRequest.getTo();
		TblAssetTransaction transaction = this.iTblAssetTransactionService.queryTblAssetTransaction(orderId, from, to);
		isNull(transaction, ErrorMessage.TRANSATION_NOT_EXIST, () -> log
				.info("cancelOrder param:{} transaction is null", JSONUtil.toJsonStr(confirmTransactionRequest)));
		isFalse(transaction.checkCancel(), ErrorMessage.TRANSATION_STATE_EX, () -> log.info("transaction: {}", JSONUtil.toJsonStr(transaction)));
		isTrue(transaction.checkSide(), ErrorMessage.CHECK_TRANSACTION_EX);
		TblAsset asset = this.iTblAssetService.info(transaction.getGoodsId());

		boolean isTopTrade = isTopTrade(asset,from);
		// 处理编号
		AssetNoMake build = AssetNoMake.builder()
				.assetId(transaction.getAssetId())
				.form(confirmTransactionRequest.getFrom())
				.to(confirmTransactionRequest.getTo())
				.transactionId(transaction.getOrderId())
				.source(PayOrderStatusEnum.PAY_SUCCESS)
				.isTopTrade(isTopTrade)
				.build();
		iTblAssetNoService.syncAssetNoUnLock(build);
		return confirmTransaction(confirmTransactionRequest, transaction);
	}
	private ConfirmOrderResponse confirmTransaction(ConfirmTransactionRequest confirmTransactionRequest, TblAssetTransaction transaction) {
		Long orderId = confirmTransactionRequest.getOrderId();
		Long from = confirmTransactionRequest.getFrom();
		Long to = confirmTransactionRequest.getTo();
		Boolean upateTransactionDealState = this.iTblAssetTransactionService
				.upateTransactionDealState(orderId, from, to, DealStateEnum.NOT_PAYED, DealStateEnum.PAID, confirmTransactionRequest.getActTotalPrice());
		isFalse(upateTransactionDealState, ErrorMessage.ORDER_OPERATE_FAILED, () -> log.info("orderId:{} confirm ex", orderId));
		TblAsset asset = this.iTblAssetService.infoWithBizIdByCache(transaction.getGoodsId());
		log.info("confirmTransaction confirmTransactionRequest:{}, TblAssetTransaction:{}", JSONUtil.toJsonStr(confirmTransactionRequest), JSONUtil.toJsonStr(transaction));
		if(!Objects.equals(OrderTypeEnum.GIFT.code(),transaction.getOrderType())) {
			if (!isTopTrade(asset,from)) {
				//非顶级交易生成铸造权
				log.info("mintPower return orderId: {}", orderId);
				return buildResponse(confirmTransactionRequest, transaction);
			} else if (Objects.equals(asset.getSecondType(), SecondEnum.ROSE.getCode())
					|| Objects.equals(asset.getFirstType(),AssetTypeEnum.BLIND_BOX.code())) {
				//玫瑰花或者盲盒，不给买方划转原始资产
				ConfirmOrderResponse response = DozerUtil.transfor(confirmTransactionRequest, ConfirmOrderResponse.class);
				response.setTs(System.currentTimeMillis());
				return response;
			}
		}


		TblAssetExt assetExt = this.iTblAssetExtService.infoWithAssetIdByCache(transaction.getAssetId());
		isNull(assetExt, ErrorMessage.ASSET_NOT_EXIST);
		TblAssetTransactionRecord accountOpertor = this.iTblAssetAccountService.plus(transaction, assetExt.referProtocol());
		if (assetExt.isErc1155()) {
			// 保存交易记录
			this.iTblAssetTransactionRecordService.save(accountOpertor);
		} else if (assetExt.isErc721()) {
			Boolean updateOwner = this.iTblAssetService
					.updateOwner(String.valueOf(confirmTransactionRequest.getFrom()), String.valueOf(confirmTransactionRequest.getTo()), transaction
							.getAssetId());
			isFalse(updateOwner, ErrorMessage.TRANSFERFROM_FAIL);
		} else {
			isFalse(Boolean.FALSE, ErrorMessage.UNSUPPORTED_REFER_PROTOCOL);
		}
		return buildResponse(confirmTransactionRequest, transaction);
	}
	private ConfirmOrderResponse buildResponse(ConfirmTransactionRequest confirmTransactionRequest, TblAssetTransaction transaction) {
		ConfirmOrderResponse response = DozerUtil.transfor(confirmTransactionRequest, ConfirmOrderResponse.class);
		response.setTs(System.currentTimeMillis());
		confirmTransactionEventPublisher.publish(transaction);
		return response;
	}

	@Override
	public TransferActionResponse transferAction(TransferActionRequest transferActionRequest) {
		String actionType = transferActionRequest.getActionType();
		TransferActionEnum transferAction = TransferActionEnum.convert(actionType);
		TblAssetTransaction transaction = iTblAssetTransactionService.queryTransaction(transferActionRequest);
		isNull(transaction, ErrorMessage.TRANSATION_NOT_EXIST, () -> log
				.info("cancelOrder param:{} transaction is null", JSONUtil.toJsonStr(transferActionRequest)));
		TransferActionResponse build = TransferActionResponse
				.builder()
				.from(Longs.tryParse(transaction.getSellerUserId()))
				.to(Longs.tryParse(transaction.getBuyerUserId()))
				.amount(transaction.getAmount().intValue())
				.bizId(transaction.getGoodsId())
				.orderNo(transaction.getOrderNo())
				.assetId(transaction.getAssetId())
				.transactionOrderId(transaction.getOrderId())
				.actionType(actionType)
				.ts(System.currentTimeMillis())
				.build();
		if (Objects.equals(TransferActionEnum.CONFIRM, transferAction)) {
			ConfirmTransactionRequest confirmTransactionRequest = ConfirmTransactionRequest
					.builder()
					.orderId(transaction.getOrderId())
					.from(Longs.tryParse(transaction.getSellerUserId()))
					.to(Longs.tryParse(transaction.getBuyerUserId()))
					.assetId(transaction.getAssetId())
					.actTotalPrice(transaction.getActTotalPrice().multiply(transaction.getAmount()))
					.build();
			ConfirmOrderResponse confirmTransaction = confirmTransaction(confirmTransactionRequest, transaction);
			AssertUtils.isNull(confirmTransaction, ErrorMessage.TRANSFTER_ACTION_EX);
			build.setResult(Objects.nonNull(confirmTransaction));
		} else if (Objects.equals(TransferActionEnum.CANCEL, transferAction)) {
			CancelTransactionRequest cancelTransactionRequest = CancelTransactionRequest
					.builder()
					.orderId(transaction.getOrderId())
					.from(Longs.tryParse(transaction.getSellerUserId()))
					.to(Longs.tryParse(transaction.getBuyerUserId()))
					.assetId(transaction.getAssetId())
					.build();
			CancelOrderResponse cancelTransaction = cancelTransaction(cancelTransactionRequest);
			AssertUtils.isNull(cancelTransaction, ErrorMessage.TRANSFTER_ACTION_EX);
			build.setResult(Objects.nonNull(cancelTransaction));
		} else {
			AssertUtils.isTrue(true, ErrorMessage.ACTION_TYPE_CHECK);
		}
		return build;
	}

	@Override
	public MyNftPage list(String platform, String uid, Boolean filterZero, Boolean needAttributes, String filterAssetType, Integer currPage, Integer pageSize) {
		List<Integer> listQueryType = AssetTypeEnum.listQueryType(filterAssetType);
		MPJLambdaWrapper<TblAssetAccount> query = JoinWrappers
				.lambda(TblAssetAccount.class)
				.selectAs(TblAsset::getId, MyNftResponse::getAssetId)
				.selectAs(TblAsset::getAssetName, MyNftResponse::getName)
				.selectAs(TblAsset::getAuthorId, MyNftResponse::getAuthorId)
				.selectAs(TblAssetAccount::getOwnerId, MyNftResponse::getOwnerId)
				.selectAs(TblAsset::getPrice, MyNftResponse::getPrice)
				.selectAs(TblAsset::getAmount, MyNftResponse::getAmount)
				.selectAs(TblAsset::getGoodsId, MyNftResponse::getBizId)
				.selectAs(TblAsset::getDescription, MyNftResponse::getDescription)
				.selectAs(TblAssetAccount::getAmount, MyNftResponse::getOwnerAmount)
				.selectAs(TblAssetAccount::getReferProtocol, MyNftResponse::getReferProtocol)
				.selectAll()
				.leftJoin(TblAsset.class, TblAsset::getId, TblAssetAccount::getAssetId)
				.selectAs(TblAssetExt::getChainTxHash, MyNftResponse::getDealHash)
				.selectAs(TblAssetExt::getChainTime, MyNftResponse::getCreateTs)
				.leftJoin(TblAssetExt.class, TblAssetExt::getAssetId, TblAsset::getId)
				.selectAs(TblAssetStaticResource::getDisplayUri, MyNftResponse::getImage)
				.leftJoin(TblAssetStaticResource.class, TblAssetStaticResource::getAssetId, TblAsset::getId)
				.eq(TblAssetAccount::getOwnerId, Longs.tryParse(uid))
				.eq(TblAsset::getAuthorId, uid)
				.eq(TblAssetAccount::getOwnerId, Longs.tryParse(uid))
//				.eq(TblAsset::getBizType, platform)
				.orderByDesc(TblAssetAccount::getUpdateTime);
		if (Objects.equals(filterZero, Boolean.TRUE)) {
			query.gt(TblAssetAccount::getAmount, BigDecimal.ZERO);
		}
		if (CollectionUtil.isNotEmpty(listQueryType)) {
			query.notIn(TblAsset::getFirstType, listQueryType);
		}
		query.orderByDesc(TblAssetAccount::getUpdateTime);
		Page<MyNftResponse> pageList = query.page(new Page<MyNftResponse>(currPage, pageSize), MyNftResponse.class);
		List<MyNftResponse> records = pageList.getRecords();
		MyNftPage build = MyNftPage
				.builder()
				.list(records)
				.total(pageList.getTotal())
				.pages(pageList.getPages())
				.pageSize(pageList.getSize())
				.curr(pageList.getCurrent())
				.build();
		if (Objects.equals(needAttributes, Boolean.TRUE)) {
			Map<Long, Object> queryAttributes = queryAttributes(records);
			build.setAttributes(queryAttributes);
		}
		return build;
	}

	@Override
	public AssetInfo assetInfo(String platform, String bizId, Long ownerId, Boolean needAttributes) {
		MPJLambdaWrapper<TblAssetAccount> query = JoinWrappers
				.lambda(TblAssetAccount.class)
				.selectAs(TblAsset::getId, MyNftResponse::getAssetId)
				.selectAs(TblAsset::getAssetName, MyNftResponse::getName)
				.selectAs(TblAsset::getAuthorId, MyNftResponse::getAuthorId)
				.selectAs(TblAssetAccount::getOwnerId, MyNftResponse::getOwnerId)
				.selectAs(TblAsset::getPrice, MyNftResponse::getPrice)
				.selectAs(TblAsset::getAmount, MyNftResponse::getAmount)
				.selectAs(TblAsset::getGoodsId, MyNftResponse::getBizId)
				.selectAs(TblAsset::getDescription, MyNftResponse::getDescription)
				.selectAs(TblAssetAccount::getAmount, MyNftResponse::getOwnerAmount)
				.selectAs(TblAssetAccount::getReferProtocol, MyNftResponse::getReferProtocol)
				.selectAll()
				.leftJoin(TblAsset.class, TblAsset::getId, TblAssetAccount::getAssetId)
				.selectAs(TblAssetExt::getChainTxHash, MyNftResponse::getDealHash)
				.selectAs(TblAssetExt::getChainTime, MyNftResponse::getCreateTs)
				.leftJoin(TblAssetExt.class, TblAssetExt::getAssetId, TblAsset::getId)
				.selectAs(TblAssetStaticResource::getDisplayUri, MyNftResponse::getImage)
				.leftJoin(TblAssetStaticResource.class, TblAssetStaticResource::getAssetId, TblAsset::getId)
				.eq(TblAsset::getGoodsId, bizId)
				.eq(TblAsset::getBizType, platform)
				.eq(TblAssetAccount::getOwnerId, ownerId);
		MyNftResponse one = query.one(MyNftResponse.class);
		if (Objects.isNull(one)) {
			return null;
		}
		AssetInfo build = AssetInfo.builder().data(one).build();
		if (Objects.equals(needAttributes, Boolean.TRUE)) {
			Map<Long, Object> queryAttributes = queryAttributes(Arrays.asList(one));
			Object object = queryAttributes.get(one.getAssetId());
			if (Objects.nonNull(object)) {
				build.setAttributes(String.valueOf(object));
			}
		}
		return build;
	}
	private Map<Long, Object> queryAttributes(List<MyNftResponse> records) {
		if (CollectionUtil.isEmpty(records)) {
			return Maps.newHashMap();
		}
		HashMap<Long, Object> resultMap = Maps.newHashMap();
		Set<Long> assetIds = records.stream().map(MyNftResponse::getAssetId).collect(toSet());
		List<TblAssetExt> assetExts = this.iTblAssetExtService.infoWithAssetIds(assetIds);
		for (TblAssetExt tblAssetExt : assetExts) {
			Long assetId = tblAssetExt.getAssetId();
			String metadata = tblAssetExt.getMetadata();
			MetaDataParams bean = JSONUtil.toBean(metadata, MetaDataParams.class);
			List<MetaDataAttribute> attributes = bean.getAttributes();
			HashMap<String, Object> itemHashMap = Maps.newHashMap();
			List<HashMap<String, Object>> newArrayList = Lists.newArrayList();
			if (CollectionUtil.isNotEmpty(attributes)) {
				attributes.forEach(x -> {
					String trait_type = x.getTrait_type();
					Object value = x.getValue();
					itemHashMap.put(trait_type, value);
					newArrayList.add(itemHashMap);
				});
			}
			resultMap.put(assetId, JSONUtil.toJsonStr(newArrayList));
		}
		return resultMap;
	}
	@Override
	public CreateGorupResult createGorup(CreateGroupRequest createGroupRequest) {
		TblAsset tblAsset = iTblAssetService.infoWithIdByCache(createGroupRequest.getAssetId());
		isNull(tblAsset, ErrorMessage.ASSET_NOT_EXIST);
		if(Objects.isNull(createGroupRequest.getOwnerId())) {
			createGroupRequest.setOwnerId(defaultAppUserId);
		} else {
			UserInfoResponse user = iInnerService.userInfo(createGroupRequest.getOwnerId());
			isNull(user, ErrorMessage.USER_NOT_EXIST);
		}

		// 根据资产ID获取交易信息
		TblAssetTransaction transaction = iTblAssetTransactionService.queryTransactionByAssetId(tblAsset.getId());
		isNull(tblAsset, ErrorMessage.ASSET_NOT_EXIST);
		return iThirdService.createGorup(createGroupRequest.getAssetId(),
				transaction.getOrderId(), createGroupRequest.getOwnerId(), tblAsset.getAssetName());
	}

	@Override
	public boolean isTopTrade(TblAsset asset, Long sellerId) {
		log.info("isTopTrade asset:{} sellerId:{}",JSONUtil.toJsonStr(asset),sellerId);
		isFalse(asset.getRootAssetId() != null && asset.getRootAssetId() > 0, ErrorMessage.ASSET_STATUS_EX, () -> cacheManager.delete(AssetCache.TBL_ASSET, asset.getGoodsId()));
		return Objects.equals(asset.getRootAssetId(), asset.getId())
				&& Objects.equals(asset.getAuthorId(), sellerId.toString());
	}
	/**
	 * 盲盒资产开盒
	 *
	 * @param blindBoxAssetId 盲盒资产id
	 * @return 开出的子资产id
	 */
	@Override
	public TblAsset unBoxAsset(Long blindBoxAssetId) {
		TblAsset blindBoxAsset = iTblAssetService.infoWithIdByCache(blindBoxAssetId);
		isNull(blindBoxAsset, ErrorMessage.ASSET_NOT_EXIST);
		long parentId = Long.parseLong(blindBoxAsset.getGoodsId());
		List<TblAsset> list = iTblAssetService.getByParentIdCacheable(parentId);
		list = list.stream().filter(asset -> !Objects.equals(asset.getParentId(),asset.getGoodsId())).toList();
		//随机选择一个
		return list.get(RandomUtil.randomInt(list.size()));
	}
}